﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Routing;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Mvc.Ajax;

namespace CoolCode.Web.MVC
{
    /// <summary>
    /// http://coolcode.cnblogs.com
    /// Bruce 
    /// JQuery Ajax扩展
    /// </summary>
    public static class JQueryHelper
    {
        #region Get

        public static string Get(this AjaxHelper helper, string linkText)
        {
            string currentActionName = (string)helper.ViewContext.RouteData.Values["action"];
            return Get(helper, linkText, currentActionName);
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName)
        {
            return Get(helper, linkText, actionName, null);
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName)
        {
            return Get(helper, linkText, actionName, controllerName, null, new AjaxOptions());
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            string jsonParam)
        {
            return Get(helper, linkText, actionName, controllerName, null, jsonParam, new AjaxOptions());
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            string jsonParam, AjaxOptions ajaxOptions)
        {
            return Get(helper, linkText, actionName, controllerName, null, jsonParam, null, ajaxOptions);
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName,
         object routeValues)
        {
            return Get(helper, linkText, actionName, routeValues, new AjaxOptions());
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName,
         object routeValues, AjaxOptions ajaxOptions)
        {
            string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
            return Get(helper, linkText, actionName, currentControllerName, routeValues, null, ajaxOptions);
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName,
           object routeValues, AjaxOptions ajaxOptions)
        {
            return Get(helper, linkText, actionName, controllerName, routeValues, null, ajaxOptions);
        }

        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues, object htmlAttributes, AjaxOptions ajaxOptions)
        {
            return Get(helper, linkText, actionName, controllerName, routeValues, null, htmlAttributes, ajaxOptions);
        }

        /// <summary>
        ///  jquery get
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="linkText"></param>
        /// <param name="actionName"></param>
        /// <param name="controllerName"></param>
        /// <param name="routeValues"></param>
        /// <param name="jsonParam"></param>
        /// <param name="htmlAttributes"></param>
        /// <param name="ajaxOptions"></param>
        /// <returns></returns>
        public static string Get(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues, string jsonParam, object htmlAttributes, AjaxOptions ajaxOptions)
        {
            return GetPostHelper(helper, linkText, actionName, controllerName, routeValues, jsonParam, htmlAttributes,
                ajaxOptions, true ); 
        }
        #endregion

        #region Post

        public static string Post(this AjaxHelper helper, string linkText)
        {
            string currentActionName = (string)helper.ViewContext.RouteData.Values["action"];
            return Post(helper, linkText, currentActionName);
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName)
        {
            return Post(helper, linkText, actionName, null);
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName)
        {
            return Post(helper, linkText, actionName, controllerName, null, new AjaxOptions());
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            string jsonParam)
        {
            return Post(helper, linkText, actionName, controllerName, null, jsonParam, new AjaxOptions());
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            string jsonParam, AjaxOptions ajaxOptions)
        {
            return Post(helper, linkText, actionName, controllerName, null, jsonParam, null, ajaxOptions);
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName,
         object routeValues)
        {
            return Post(helper, linkText, actionName, routeValues, new AjaxOptions());
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName,
         object routeValues, AjaxOptions ajaxOptions)
        {
            string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
            return Post(helper, linkText, actionName, currentControllerName, routeValues, null, ajaxOptions);
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName,
           object routeValues, AjaxOptions ajaxOptions)
        {
            return Post(helper, linkText, actionName, controllerName, routeValues, null, ajaxOptions);
        }

        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues, object htmlAttributes, AjaxOptions ajaxOptions)
        {
            return Post(helper, linkText, actionName, controllerName, routeValues, null, htmlAttributes, ajaxOptions);
        }

        /// <summary>
        ///  jquery post
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="linkText"></param>
        /// <param name="actionName"></param>
        /// <param name="controllerName"></param>
        /// <param name="routeValues"></param>
        /// <param name="jsonParam"></param>
        /// <param name="htmlAttributes"></param>
        /// <param name="ajaxOptions"></param>
        /// <returns></returns>
        public static string Post(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues, string jsonParam, object htmlAttributes, AjaxOptions ajaxOptions)
        {
            return GetPostHelper(helper, linkText, actionName, controllerName, routeValues, jsonParam, htmlAttributes,
                ajaxOptions, false); 
        }

        private static string GetPostHelper( AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues, string jsonParam, object htmlAttributes, AjaxOptions ajaxOptions, bool isGet)
        {
            string linkFormat = "<a href=\"{0}\" {1} {3}>{2}</a>";
            string atts = string.Empty;
            string ajaxs = string.Empty;
            string opt = isGet ? "get" : "post";
            if (htmlAttributes != null)
                atts = Html.ConvertObjectToAttributeList(htmlAttributes);
            UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
            string action = routeValues == null ? url.Action(actionName, controllerName) : url.Action(actionName, controllerName, routeValues, "");
            if (string.IsNullOrEmpty(jsonParam))
                jsonParam = "$('form').serialize()";
            if (ajaxOptions != null)
            {
                string confirmScript = string.Empty;
                if (!string.IsNullOrEmpty(ajaxOptions.Confirm))
                {
                    confirmScript = string.Format("var result = confirm('{0}'); if(!result)return false;", ajaxOptions.Confirm);
                }
                ajaxs = string.Format("onclick=\"{0}$.{1}('{2}',{3},{4});return false;\" ",
                   confirmScript + ajaxOptions.OnBegin,
                   opt,
                    action,
                    jsonParam,
                  "function(data){" + ajaxOptions.OnSuccess + "}"
                 );
            }
            string result = string.Format(CultureInfo.InvariantCulture, linkFormat, "#", atts, linkText, ajaxs);
            return result;
        }
        #endregion 

        #region Load ：ajax请求，callback：把结果显示在html标签中

        public static string Load(this AjaxHelper helper, string el)
        {
            return Load(helper, el, (string)helper.ViewContext.RouteData.Values["action"]);
        }

        public static string Load(this AjaxHelper helper, string el, string actionName)
        {
            return Load(helper, el, actionName, null);
        }

        public static string Load(this AjaxHelper helper, string el, string actionName, object routeValues)
        {
            string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
            return Load(helper, el, actionName, currentControllerName, routeValues);
        }

        /// <summary>
        ///  jquery load
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="el">el 可以是标签的id或是JQuery形式： #xx 表示id，.xx 表示样式...详细的请参考JQuery的相关文档</param>
        /// <param name="actionName"></param>
        /// <param name="controllerName"></param>
        /// <param name="routeValues"></param>
        /// <returns></returns>
        public static string Load(this AjaxHelper helper, string el, string actionName, string controllerName, object routeValues)
        {
            if (string.IsNullOrEmpty(el))
                throw new ArgumentNullException("el");
            UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
            string action = routeValues == null ? url.Action(actionName, controllerName) : url.Action(actionName, controllerName, routeValues, "");
            /*如果以英文开头则认为是id*/
            if (el[0] > 'A' && el[0] < 'z')
                el = "#" + el;
            return string.Format("$('{0}').load('{1}');", el, action);
        }

        #endregion

        #region Ajax Upload


        public static string Upload(this AjaxHelper helper, string linkText, string actionName )
        {
            string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
            return Upload(helper, linkText, actionName, currentControllerName, null, null, new AjaxOptions());
        }

        public static string Upload(this AjaxHelper helper, string linkText, string actionName, string controllerName,
            object routeValues,  object htmlAttributes, AjaxOptions ajaxOptions)
        {
            string id = "uploadid";
            string linkFormat = "<a href=\"{0}\" {1} {3}>{2}</a>";
            string atts = string.Empty;
            string ajaxs = string.Empty;
            if (htmlAttributes != null)
                atts = Html.ConvertObjectToAttributeList(htmlAttributes);
            atts += string.Format(" id='{0}' ",  id);
            UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
            string action = routeValues == null ? url.Action(actionName, controllerName) : url.Action(actionName, controllerName, routeValues, "");
            if (ajaxOptions != null)
            {
                string confirmScript = string.Empty;
                if (!string.IsNullOrEmpty(ajaxOptions.Confirm))
                {
                    confirmScript = string.Format("var result = confirm('{0}'); if(!result)return false;", ajaxOptions.Confirm);
                }
                //ajaxs = string.Format("onclick=\"{0} new AjaxUpload('{1}', {action: '{1}'});.('{2}',{3},{4});return false;\" ",
                //   confirmScript + ajaxOptions.OnBegin, 
                //    action,
                //    jsonParam,
                //  "function(data){" + ajaxOptions.OnSuccess + "}"
                // );
            }
            string result = string.Format(CultureInfo.InvariantCulture, linkFormat, "#", atts, linkText, ajaxs);
            //result += string.Format( "<script>new AjaxUpload('{0}', {action: '{1}'});</script>", id, action);
            result += "<script type=\"text/javascript\"> $(document).ready(function() { new AjaxUpload('#" + id + "', {action: '" + action + "'});});</script>";
            return result;
        }

        #endregion

    }

    #region 重典 对jquery的扩展
    /// <summary>
    /// http://chsword.cnblogs.com
    /// 重典 
    /// QQ:77298666
    /// </summary>
    public static class AjaxJquery
    {
        /// <summary>
        /// 就是一个元素，原来显示就让它隐藏，原来隐藏就让他显示
        /// </summary>
        /// <param name="a"></param>
        /// <param name="linkText"></param>
        /// <param name="ToggleID"></param>
        /// <returns></returns>
        static public String JToggle(this HtmlHelper a, string linkText, String ToggleID)
        {
            return a.JToggle(linkText, ToggleID, null);
        }

        /// <summary>
        /// 就是一个元素，原来显示就让它隐藏，原来隐藏就让他显示
        /// </summary>
        /// <param name="a"></param>
        /// <param name="linkText"></param>
        /// <param name="ToggleID"></param>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        static public String JToggle(this HtmlHelper a, string linkText, String ToggleID, object htmlAttributes)
        {
            string linkFormat = "<a href=\"{0}\" {1} {3}>{2}</a>";
            string atts = string.Empty;
            string ajaxs = string.Empty;
            if (htmlAttributes != null)//由object 序列成key="value" 这样的属性模式
                atts = Html.ConvertObjectToAttributeList(htmlAttributes);

            ajaxs = string.Format("onclick=\"$('{0}').toggle();\" ",
                ToggleID[0] == '#' ? ToggleID : "#" + ToggleID//Jquery里id不是要#嘛得验一下个人的那啥不同啊
                );
            string result = string.Format(CultureInfo.InvariantCulture, linkFormat, "#", atts,  linkText, ajaxs);
            return result;
        }

        /// <summary>
        /// 生成一个可以加载地址的Ajax连接
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="a"></param>
        /// <param name="action"></param>
        /// <param name="linkText"></param>
        /// <param name="ajaxOptions"></param>
        /// <returns></returns>
        static public string JLoadLink<T>(this AjaxHelper a, System.Linq.Expressions.Expression<Action<T>> action, string linkText, AjaxOptions ajaxOptions) where T : Controller
        {
            return a.JLoadLink<T>(action, linkText, null, ajaxOptions);
        }

        /// <summary>
        /// 生成一个可以加载地址的Ajax连接
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="a"></param>
        /// <param name="action"></param>
        /// <param name="linkText"></param>
        /// <param name="htmlAttributes"></param>
        /// <param name="ajaxOptions"></param>
        /// <returns></returns>
        static public string JLoadLink<T>(this AjaxHelper a, System.Linq.Expressions.Expression<Action<T>> action, string linkText, object htmlAttributes, AjaxOptions ajaxOptions) where T : Controller
        {
            string linkFormat = "<a href=\"{0}\" {1} {3}>{2}</a>";
            string atts = string.Empty;
            string ajaxs = string.Empty;
            if (htmlAttributes != null)
                atts = Html.ConvertObjectToAttributeList(htmlAttributes);
            string link = LinkBuilder.BuildUrlFromExpression<T>(a.ViewContext.RequestContext, action);
            //* */a.BuildUrlFromExpression<T>(action);
            if (ajaxOptions != null)
            {
                ajaxs = string.Format("onclick=\"{0};$ActionLink('{1}','{2}');return false;\" ",
                    ajaxOptions.OnBegin,//$ActionLink这个方法定义在common.js里 
                    link,
                    ajaxOptions.UpdateTargetId
                 );
            }
            string result = string.Format(CultureInfo.InvariantCulture, linkFormat, "#", atts,  linkText , ajaxs);
            return result;
        }
    }
    #endregion

    #region MVC lib
    public static class Html
    {
        /// <summary>
        /// 从MVC工程里搞出来的，原来有程序集保护用不了。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ConvertObjectToAttributeList(object value)
        {
            StringBuilder sb = new StringBuilder();
            if (value != null)
            {
                IDictionary<string, object> d = value as IDictionary<string, object>;
                if (d == null)
                {
                    d = new RouteValueDictionary(value);
                }

                string resultFormat = "{0}=\"{1}\" ";
                foreach (string attribute in d.Keys)
                {
                    object thisValue = d[attribute];
                    if (d[attribute] is bool)
                    {
                        thisValue = d[attribute].ToString().ToLowerInvariant();
                    }
                    sb.AppendFormat(resultFormat, attribute.Replace("_", "").ToLowerInvariant(), thisValue);
                }
            }
            return sb.ToString();
        }
    }

    /// <summary>
    /// Creates url that can be used in a link to a controller action, based on an
    /// Expression containing a call to the action with the required parameters
    /// </summary>
    /// <remarks>
    /// This is based on the LinkBuilder.BuildUrlFromExpression method within MVC preview 4.
    /// However the original method requires a ViewContext which is not available when the 
    /// method is invoked from within a Controller method.  
    /// </remarks>
    public static class LinkBuilder
    {
        public static string GetActionUrl<T>(RequestContext context, Expression<Action<T>> action) where T : Controller
        {
            return BuildUrlFromExpression(context, action);
        }

        public static string BuildUrlFromExpression<T>(RequestContext context, Expression<Action<T>> action) where T : Controller
        {
            RouteValueDictionary values = ExpressionHelper.GetRouteValuesFromExpression(action);
            VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(context, values);
            return (vpd == null) ? null : vpd.VirtualPath;
        }
    }

    internal static class ExpressionHelper
    {
        public static RouteValueDictionary GetRouteValuesFromExpression<T>(Expression<Action<T>> action) where T : Controller
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            MethodCallExpression body = action.Body as MethodCallExpression;
            if (body == null)
            {
                throw new ArgumentException("Expression must be a method call", "action");
            }
            string name = typeof(T).Name;
            if (!name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Controller name must end in 'Controller'", "action");
            }
            name = name.Substring(0, name.Length - "Controller".Length);
            if (name.Length == 0)
            {
                throw new ArgumentException("Cannot route to class named 'Controller'", "action");
            }
            RouteValueDictionary rvd = new RouteValueDictionary();
            rvd.Add("Controller", name);
            rvd.Add("Action", body.Method.Name);
            AddParameterValuesFromExpressionToDictionary(rvd, body);
            return rvd;
        }

        private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
        {
            ParameterInfo[] parameters = call.Method.GetParameters();
            if (parameters.Length > 0)
            {
                for (int i = 0; i < parameters.Length; i++)
                {
                    Expression expression = call.Arguments[i];
                    object obj2 = null;
                    ConstantExpression expression2 = expression as ConstantExpression;
                    if (expression2 != null)
                    {
                        obj2 = expression2.Value;
                    }
                    else
                    {
                        Expression<Func<object>> expression3 = Expression.Lambda<Func<object>>(Expression.Convert(expression, typeof(object)), new ParameterExpression[0]);
                        obj2 = expression3.Compile()();
                    }
                    rvd.Add(parameters[i].Name, obj2);
                }
            }
        }

    }
    #endregion
}
